package com.derbysoft.synxisadapter.commons.repository.impl;

import com.derbysoft.core.CriteriaCreator;
import com.derbysoft.core.Limit;
import com.derbysoft.core.LimitedList;
import com.derbysoft.core.Sorter;
import com.derbysoft.core.utils.DateTimeUtils;
import com.derbysoft.synxisadapter.commons.model.TransactionLog;
import com.derbysoft.synxisadapter.commons.repository.TransactionLogRepository;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

public class TransactionLogRepositoryImpl extends BaseRepositorySupport implements TransactionLogRepository {

    private static Logger logger = LoggerFactory.getLogger(TransactionLogRepositoryImpl.class);

    @Override
    public LimitedList<TransactionLog> findByCondition(final TransactionLogCondition condition, Limit limit) {
        List<Object> params = new ArrayList<Object>();
        String hql = populateHql(condition, params);
        return dataAccessor.findByHql(
                TransactionLog.class, hql, params.toArray(), limit, new Sorter().desc("id"));
    }

    @Override
    public LimitedList<TransactionLog> findByCondition(DMXTransactionLogCondition condition, Limit limit) {
        List<Object> params = new ArrayList<Object>();
        String hql = populateHql(condition, params);
        return dataAccessor.findByHql(
                TransactionLog.class, hql, params.toArray(), limit, new Sorter().desc("id"));
    }

    @Override
    public TransactionLog findById(Long id) {
        return dataAccessor.get(TransactionLog.class, id);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    @Override
    public void create(TransactionLog transactionLog) {
        dataAccessor.save(transactionLog);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    @Override
    public int removeExpired(final int keepLogDays, final boolean error, int batchSize) {
        logger.info("Begin removeLog keepLogDays:{} error:{}", keepLogDays, error);

        List<TransactionLog> transactionLogs = dataAccessor.findByCriteria(TransactionLog.class, new CriteriaCreator() {
            @Override
            public Criteria createCriteria(Session session) {
                Criteria criteria = session.createCriteria(TransactionLog.class);
                criteria.add(Restrictions.lt("timestamp", DateTimeUtils.offsetDays(-keepLogDays)));
                criteria.add(Restrictions.eq("error", error));
                return criteria;
            }
        }, new Limit(0, batchSize), new Sorter().desc("timestamp"));
        dataAccessor.deleteAll(transactionLogs);
        logger.info("End removeLog Count:{}", transactionLogs.size());
        return transactionLogs.size();
    }

    private String populateHql(DMXTransactionLogCondition condition, List<Object> params) {
        StringBuilder sb = new StringBuilder();
        sb.append("FROM TransactionLog WHERE 1=1");

        if (condition.getOperationType() != null) {
            sb.append(" AND operationType=?");
            params.add(condition.getOperationType());
        }

        if (condition.getAfter() != null) {
            sb.append(" AND timestamp<?");
            params.add(DateTimeUtils.offsetDays(condition.getAfter(),1));
        }

        if (condition.getBefore() != null) {
            sb.append(" AND timestamp>=?");
            params.add(condition.getBefore());
        }

        if (condition.getMinSpentTime() != null) {
            sb.append(" AND timespan>=?");
            params.add(condition.getMinSpentTime());
        }

        if (condition.getMaxSpentTime() != null) {
            sb.append(" AND timespan<=?");
            params.add(condition.getMaxSpentTime());
        }

        if (condition.getErrorFilter() != null) {
            if (condition.getErrorFilter() == DMXTransactionLogCondition.ErrorFilter.HAS_ERROR) {
                sb.append(" AND error=?");
                params.add(true);
            } else if (condition.getErrorFilter() == DMXTransactionLogCondition.ErrorFilter.NO_ERROR) {
                sb.append("  AND error=?");
                params.add(false);
            }
        }

        if (condition.getKeyWord() != null) {
            sb.append(" AND id IN (" +
                    "SELECT transactionLog FROM TransactionDetail WHERE detail LIKE ?");
            params.add(String.format("%%%s%%", condition.getKeyWord()));

            if (condition.getDirection() != null) {
                sb.append(" AND direction=?");
                params.add(condition.getDirection());
            }

            sb.append(")");
        }

        return sb.toString();
    }

    private String populateHql(TransactionLogCondition condition, List<Object> params) {
        StringBuilder sb = new StringBuilder();
        sb.append("FROM TransactionLog WHERE 1=1");

        if (condition.getOperationType() != null) {
            sb.append(" AND operationType=?");
            params.add(condition.getOperationType());
        }

        if (condition.getAfter() != null) {
            sb.append(" AND timestamp>=?");
            params.add(condition.getAfter());
        }

        if (condition.getBefore() != null) {
            sb.append(" AND timestamp<?");
            params.add(DateTimeUtils.offsetDays(condition.getBefore(), 1));
        }

        if (condition.getTimespan() != null) {
            sb.append(" AND timespan>?");
            params.add(condition.getTimespan());
        }

        if (condition.getErrorFilter() != null) {
            if (condition.getErrorFilter() == TransactionLogCondition.ErrorFilter.HAS_ERROR) {
                sb.append(" AND error=?");
                params.add(true);
            } else if (condition.getErrorFilter() == TransactionLogCondition.ErrorFilter.NO_ERROR) {
                sb.append("  AND error=?");
                params.add(false);
            }
        }

        if (condition.getKeyWord() != null) {
            sb.append(" AND id IN (" +
                    "SELECT transactionLog FROM TransactionDetail WHERE detail LIKE ?");
            params.add(String.format("%%%s%%", condition.getKeyWord()));

            if (condition.getDirection() != null) {
                sb.append(" AND direction=?");
                params.add(condition.getDirection());
            }

            sb.append(")");
        }

        return sb.toString();
    }
}
